#include "bootmem.h"
#include "page.h"
#include "mmzone.h"
#include "printk.h"
#include "mm.h"

// bootmem 分配器中保留一段内存区域，防止这段内存被后续分配
void reserve_bootmem_core(bootmem_data_t *bdata, unsigned long addr, unsigned long size)
{
	unsigned long i;
	unsigned long sidx = (addr - bdata->node_boot_start) / PAGE_SIZE;
	unsigned long eidx = (addr + size - bdata->node_boot_start + PAGE_SIZE - 1) / PAGE_SIZE;
	unsigned long end = (addr + size + PAGE_SIZE - 1) / PAGE_SIZE;

	if (!size)
		BUG();

	if (end > bdata->node_low_pfn)
		BUG();

	for (i = sidx; i < eidx; i++)
	{
		// 测试并设置位图中的位，如果已经设置过（已保留），打印警告
		if (test_and_set_bit(i, bdata->node_bootmem_map))
			printk("page %d reserved twice.\n", i * PAGE_SIZE);
	}
}

unsigned long free_all_bootmem_core(pg_data_t *pgdat)
{
	struct page *page = pgdat->node_mem_map;
	bootmem_data_t *bdata = pgdat->bdata;
	unsigned long i, count, total = 0;
	unsigned long idx;

	count = 0;
	// 计算当前节点下的总页面数量
	idx = bdata->node_low_pfn - (bdata->node_boot_start >> PAGE_SHIFT);
	for (i = 0; i < idx; i++, page++)
	{
		// 检查当前页面是否为保留页（位图中对应位为0表示未保留）
		if (!test_bit(i, bdata->node_bootmem_map))
		{
			count++;
			// 清除页面的保留标志
			ClearPageReserved(page);
			// 设置页面引用计数为1,__free_page会通过put_page_testzero来减少引用计数，只有在引用计数为0时才真正释放该页
			set_page_count(page, 1);
			// 将页面释放到伙伴系统
			__free_page(page);
		}
	}
	total += count;

	// 释放bootmem位图本身占用的页面
	page = virt_to_page(bdata->node_bootmem_map);
	count = 0;
	// 计算位图占用的页面数（向上取整）
	for (i = 0; i < ((bdata->node_low_pfn - (bdata->node_boot_start >> PAGE_SHIFT)) / 8 + PAGE_SIZE - 1) / PAGE_SIZE; i++, page++)
	{
		count++;
		ClearPageReserved(page);
		set_page_count(page, 1);
		__free_page(page);
	}
	total += count;
	bdata->node_bootmem_map = NULL;

	return total;
}